const defaultHeight = 32;
const rows = 10;
import DesignComponents from '@/components/form/DesignComponents';

class DavidTable {
  constructor(rootElementId, configs) {
    this.rootElementId = rootElementId;
    this.configs = configs;

    if (this.configs && this.configs.onlyBind) {
      this.resolveConfigFromElements();
    } else {
      this.clearContainer();
      this.resolveDefaultConfigValue();
      this.renderTable();
    }

    this.colBarPlaceholder = null;
    this.bind();
  }

  bind() {
    this.bindResize();
    this.bindSelect();
    this.bindEdit();
  }

  bindResize() {
    $(`#${this.rootElementId} .col-bar`).off('mousedown').on('mousedown', e => this.colBarDragstart(e));
    $(`#${this.rootElementId} .row-bar`).off('mousedown').on('mousedown', e => this.rowBarDragstart(e));
  }

  bindSelect() {
    $(`#${this.rootElementId} td`).off('mousedown').on('mousedown', e => this.tdSelectStart(e));
    $(`#${this.rootElementId} td`).off('contextmenu').on('contextmenu', e => this.tdContext(e));
  }

  bindEdit() {
    $(`#${this.rootElementId} td`).off('dblclick').on('dblclick', e => this.tdStartEdit(e));
  }

  clearSelection(e) {
    this.selection = null;
    $(`#${this.rootElementId} td`).removeClass('selecting-td');
    this.invokeClearSelection();
  }

  invokeClearSelection() {
    if (this.configs.onClearSelection) {
      this.configs.onClearSelection();
    }
  }

  tdContext(e) {
    if (this.selection && this.isSelecting(this.getPosition(e.target))) {
      this.openSelectionContextMenu(e);
    }
    return false;
  }

  openSelectionContextMenu(e) {
    if (this.contextWindow) {
      this.contextWindow.close();
      this.contextWindow = null;
    }
    let that = this;
    let contextWindow = this.contextWindow = $.contextMenu({
      left: e.clientX,
      top: e.clientY,
      menu: [
        {
          itemName: '合并',
          click(e) {
            that.mergeSelection();
            that.prevent(e);
            return true;
          },
        },
        {
          itemName: '取消合并',
          click(e) {
            that.disassembleSelection();
            that.prevent(e);
            return true;
          }
        },
        {
          itemName: '插入一列',
          click(e) {
            that.insertLeftCol();
            that.prevent(e);
            return true;
          }
        },
        {
          itemName: '插入一行(在上方)',
          click(e) {
            that.insertTopRow();
            that.prevent(e);
            return true;
          }
        },
        {
          itemName: '插入一行(在下方)',
          click(e) {
            that.insertBottomRow();
            that.prevent(e);
            return true;
          }
        },
        {
          itemName: '删除整行',
          click(e) {
            that.removeTopRow();
            that.prevent(e);
            return true;
          }
        },
        {
          itemName: '删除整列',
          click(e) {
            that.removeLeftCol();
            that.prevent(e);
            return true;
          }
        },
        {
          itemName: '清除样式',
          click(e) {
            that.clearStyle();
            return true;
          }
        },
      ]
    });

    function fun(e) {
      contextWindow.close();
      that.prevent(e);
      $(document).unbind('click', fun);
    }

    $(document).on('click', fun);
  }

  removeLeftCol() {
    let miny = Math.min(...this.selection.selecting.map(m => m.y));
    let maxy = Math.max(...this.selection.selecting.map(m => m.y));
    while (maxy >= miny) {
      this.removeCol(maxy--);
    }
    this.removed();
  }

  removeTopRow() {
    let minx = Math.min(...this.selection.selecting.map(m => m.x));
    let maxx = Math.max(...this.selection.selecting.map(m => m.x));
    while (minx <= maxx) {
      this.removeRow(maxx--);
    }
    this.removed();
  }

  insertTopRow() {
    let minx = Math.min(...this.selection.selecting.map(m => m.x));
    this.insertRow(minx, 'before');
    this.inserted();
  }

  insertBottomRow() {
    let maxX = Math.max(...this.selection.selecting.map(m => m.x));
    this.insertRow(maxX, 'after');
    this.inserted();
  }

  insertLeftCol() {
    let miny = Math.min(...this.selection.selecting.map(m => m.y));
    this.insertCol(miny);
  }

  insertRow(rowIndex, insertType) {
    let trs = $(`#${this.rootElementId} tr`);
    let height = defaultHeight;
    let tr = trs[rowIndex];
    let newTr = null;

    if (tr) {
      this.configs.rowHeight.splice(rowIndex, 0, height);
      newTr = this.createTr(rowIndex);
      this.resolveDataRole(newTr, rowIndex);
    } else { //当前插入的是最后一行
      this.configs.rowHeight.push(height);
      tr = trs[rowIndex - 1];
      newTr = this.createTr(rowIndex);
      this.resolveDataRole(newTr, rowIndex - 1);
    }

    if (insertType === 'before') {
      $(tr).before($(newTr));
    } else {
      $(tr).after($(newTr));
    }


    this.configs.row++;
    this.resolveTdPosition();
    this.reDrewBars();
    this.bindSelect();
    this.bindResize();
    this.refreshMerges();
    this.clearSelection();
    this.heightChange();
    this.bindEdit();
    this.inserted();
  }

  resolveDataRole(newTr, rowIndex) {
    let trs = $(`#${this.rootElementId} tr`);
    let tr = trs[rowIndex];
    if (!tr) return;
    let dataRole = this.getDataRole(tr);
    if (dataRole && dataRole !== '') {
      $(newTr).attr('data-role', dataRole);
    }

    if (dataRole === 'subtable-detail') {
      $($(newTr).find('td')[0]).html('明细区');
    }
  }

  getDataRole(ele) {
    return $(ele).attr('data-role');
  }

  removeCol(colIndex) {
    if (this.configs.col === 1) return;
    let trs = $(`#${this.rootElementId} tr`);
    for (let i = 0; i < trs.length; i++) {
      $($(trs[i]).find('td')[colIndex]).remove();
    }
    let cols = $(`#${this.rootElementId} colgroup col`);
    $(cols[colIndex]).remove();
    this.configs.col--;
    this.configs.colWidth.splice(colIndex, 1);
    this.resolveTdPosition();
    this.refreshMerges();

    this.refreshWidthKeepPercent();
  }

  removeRow(rowIndex) {
    if (this.configs.row === 1) return;
    let trs = $(`#${this.rootElementId} tr`);
    $(trs[rowIndex]).remove();
    this.configs.row--;
    this.configs.rowHeight.splice(rowIndex, 1);
    this.resolveTdPosition();
    this.refreshMerges();
  }

  insertCol(colIndex) {
    let trs = $(`#${this.rootElementId} tr`);
    let set = new Set(), flag = false;
    for (let i = 0; i < trs.length; i++) {
      let tds = $(trs[i]).find('td');
      let td = tds[colIndex];

      if (this.isTdShow(td)) {
        $(td).before($(this.createTd(0, 0)));
      } else {
        flag = true;
        $(td).before($(this.createTd(0, 0)));
      }
    }

    this.insertColToColGroup(colIndex);
    this.configs.col++;
    this.resolveTdPosition();
    this.clearSelection();
    this.reDrewBars();
    this.bindSelect();
    this.bindResize();
    this.bindEdit();

    if (flag) {
      this.refreshMerges();
    }
  }

  refreshMerges() {
    $(`#${this.rootElementId} td`).css('display', '');
    this.forEachTds((row, col, td) => {
      if (!this.isTdShow(td)) return;
      let span = this.getTdSpan(td);
      if (span.rowspan === 1 && span.colspan === 1) return;

      let minx = row;
      let miny = col;
      let maxx = row + span.rowspan;
      let maxy = col + span.colspan;

      let first = true;
      for (let i = minx; i < maxx; i++) {
        for (let j = miny; j < maxy; j++) {
          if (first) {
            first = false;
            continue;
          }
          let td = this.getTdByPoint({x: i, y: j});
          this.hideTd(td);
        }
      }
      this.showTd(td);
    });
  }

  resolveTdPosition() {
    let trs = $(`#${this.rootElementId} tr`);
    for (let i = 0; i < trs.length; i++) {
      let tds = $(trs[i]).find(`td`);
      for (let j = 0; j < tds.length; j++) {
        $(tds[j]).attr('data-pointion', `${i}-${j}`);
      }
    }
  }

  insertColToColGroup(colIndex) {
    let list = this.configs.colWidth;
    let originWidth = list[colIndex];
    list[colIndex] = parseInt(list[colIndex] / 2);
    let width = originWidth - list[colIndex];

    list.splice(colIndex, 0, width);

    let goup = $(`#${this.rootElementId} colgroup`)[0];

    let newGroup = this.createColgroup();
    goup.replaceWith(newGroup);
  }

  reDrewBars() {
    let barContainer = $(`#${this.rootElementId} .bar-container`)[0];
    barContainer.replaceWith(this.createBarsContainer());
  }

  disassembleSelection() {
    let minx = Math.min(...this.selection.selecting.map(m => m.x));
    let miny = Math.min(...this.selection.selecting.map(m => m.y));

    let maxx = Math.max(...this.selection.selecting.map(m => m.x));
    let maxy = Math.max(...this.selection.selecting.map(m => m.y));

    for (let i = minx; i <= maxx; i++) {
      for (let j = miny; j <= maxy; j++) {
        let td = this.getTdByPoint({x: i, y: j});
        this.showTd(td);
        $(td).removeAttr('colspan');
        $(td).removeAttr('rowspan');
      }
    }

    this.reComputeHeights();
    this.refreshBarContainer();
    this.disassembled();
  }

  mergeSelection() {
    let minx = Math.min(...this.selection.selecting.map(m => m.x));
    let miny = Math.min(...this.selection.selecting.map(m => m.y));

    let maxx = Math.max(...this.selection.selecting.map(m => m.x));
    let maxy = Math.max(...this.selection.selecting.map(m => m.y));

    let first = true;
    for (let i = minx; i <= maxx; i++) {
      for (let j = miny; j <= maxy; j++) {
        if (first) {
          first = false;
          continue;
        }
        let td = this.getTdByPoint({x: i, y: j});
        this.hideTd(td);
      }
    }

    let td = this.getTdByPoint({x: minx, y: miny});
    this.showTd(td);
    $(td).attr('colspan', maxy - miny + 1);
    $(td).attr('rowspan', maxx - minx + 1);
    this.reComputeHeights();
    this.refreshBarContainer();
    this.merged();
  }

  tdStartEdit(e) {
    let td = e.target;
    // console.log(td)
    if ($(td).children()[0]) return;
    this.editableTd(e.target);
    $(td).focus();

    let that = this;

    function fun() {
      that.removeEditTd(e.target);
      that.reComputeHeights();
      that.heightChange();
      $(td).unbind('blur', fun);
      that.edited();
    }

    $(td).on('blur', fun);

    this.clearSelection();
    this.prevent(e);
  }

  clearEdit() {
    $(`#${this.rootElementId} td`).removeAttr('contentEditable');
  }

  reComputeHeights() {
    let trs = $(`#${this.rootElementId} tr`);

    for (let i = 0; i < trs.length; i++) {
      if ($(trs[i]).height() === 73) {
        let tr = $(trs[i]);
        tr = $(trs[i]);
      }
      this.configs.rowHeight[i] = $(trs[i]).height();
    }
  }

  tdSelectStart(e) {
    if (!this.hasPosition(e.target)) {
      return;
    }
    if (e.buttons === 2 && this.selection) {
      this.prevent(e);
      return;
    }
    if (this.isEditable(e.target)) {
      return;
    } else {
      this.clearEdit();
    }

    if (!this.isLeftMouseButton(e)) return;
    this.initSelection(e.target);
    this.expanSelection();
    this.drawSelection();

    let that = this;

    function enterFunction(e) {
      that.tdSelecting(e);
    }

    $(`#${this.rootElementId} td`).off('mouseenter').on('mouseenter', enterFunction);

    function mouseupFunction(e) {
      $(`#${that.rootElementId} td`).unbind('mouseenter', enterFunction);
      $(document).unbind('mouseup', mouseupFunction);
      that.prevent(e);
      that.bindClearFunction();
    }

    function mouseup() {
      that.invokeSelected();
      $(`#${this.rootElementId} td`).unbind('mouseup', mouseup);
    }

    $(`#${this.rootElementId} td`).off('mouseup').on('mouseup', mouseup);
    $(document).on('mouseup', mouseupFunction);
    this.prevent(e);
  }

  invokeSelected() {
    if (this.configs.onSelected && this.selection) {
      this.resolveRealShowTds();
      this.mergeRealShowTdStyle();
      this.configs.onSelected(this.selection);
    }
  }

  mergeRealShowTdStyle() {
    let props = ['background-color', 'font-size', 'font-weight',
      'color', 'font-style', 'vertical-align', 'text-align'];
    let tds = this.selection.realShowTd;
    let res = {};
    for (let i = 0; i < tds.length; i++) {
      let td = tds[i];
      $(td).removeClass('selecting-td');
      for (let j = 0; j < props.length; j++) {
        let value = $(td).css(props[j]);
        if (td.style[props[j]] && td.style[props[j]] !== '') {
          value = td.style[props[j]];
        }
        if (props[j].indexOf('color') !== -1) {
          value = $.getHexColor(value);
        }
        if (res[props[j]] === undefined) {
          res[props[j]] = value;
        } else {
          if (res[props[j]] === value) {

          } else {
            res[props[j]] = '';
          }
        }
      }
      $(td).addClass('selecting-td');
    }

    this.selection.mergedStyle = res;
  }

  resolveRealShowTds() {
    let selecting = this.selection.selecting, tds = [];
    for (let i = 0; i < selecting.length; i++) {
      let point = selecting[i];
      let td = this.getTdByPoint(point);
      if (this.isTdShow(td)) {
        tds.push(td);
      }
    }
    this.selection.realShowTd = tds;
  }

  bindClearFunction() {
    let that = this;
    if (!that.selectCancelTempFunction) {
      that.selectCancelTempFunction = function (e) {
        if (that.isIgnoreMouseEvent(e.target)) {
          return;
        }
        that.clearSelection(e);
        $(document).unbind('mousedown', that.selectCancelTempFunction);
        that.selectCancelTempFunction = null;
      };
      $(document).on('mousedown', that.selectCancelTempFunction);
    }
  }

  isIgnoreMouseEvent(ele) {
    while (ele && $(ele).attr('data-ignore-mouse-event') !== 'true') {
      ele = ele.parentNode;
    }
    return !!ele;
  }

  initSelection(td) {
    this.selection = {
      selecting: [this.getPosition(td)],
      startPoint: this.getPosition(td),
      endPoint: null
    };
  }

  setSelection(selection) {
    this.selection = selection;
    this.expanSelection();
    this.drawSelection();
    this.bindClearFunction();
  }

  setSelectionStyle(style) {
    let selecting = this.selection.realShowTd;
    for (let i = 0; i < selecting.length; i++) {
      let td = selecting[i];
      for (let key in style) {
        let originStyle = $(td).css(key), value = style[key];
        if (originStyle !== value) {
          $(td).css(key, value);
        }
      }
    }

    this.resolveBlankWidth();
    this.reComputeHeights();
    this.refreshBarContainer();
  }

  tdSelecting(e) {
    let target = e.target;
    if (!this.hasPosition(target)) {
      target = this.tryGetPositionTd(target);
      if (!target) return;
    }
    this.selection.endPoint = this.getPosition(target);
    this.selection.selecting = [];
    this.addSelectPoint(this.selection.startPoint);
    this.addSelectPoint(this.selection.endPoint);
    this.expanSelection();
    this.drawSelection();
    this.prevent(e);
  }

  drawSelection() {
    $(`#${this.rootElementId} td`).removeClass('selecting-td');
    let list = this.selection.selecting;
    for (let i = 0; i < list.length; i++) {
      $(this.getTdByPoint(list[i])).addClass('selecting-td');
    }
  }

  addSelectPoint(point) {
    let index = this.selection.selecting.findIndex(p => {
      return p.x === point.x && p.y === point.y;
    });
    if (index === -1) {
      this.selection.selecting.push(point);
    }
  }

  expanSelection() {
    this.baseExpanSelection();
    let tempLen = this.selection.selecting.length;
    while (true) {
      for (let i = 0; i < this.selection.selecting.length; i++) {
        let point = this.selection.selecting[i];
        if (!this.isShow(point)) {
          point = this.getShowPointByPoint(point);

        }
        this.expanByCell(point);
        this.baseExpanSelection();
      }

      if (this.selection.selecting.length === tempLen) break;
      tempLen = this.selection.selecting.length;
    }
  }

  getShowPointByPoint(point) {
    for (let i = 0; i <= point.x; i++) {
      for (let j = 0; j <= point.y; j++) {
        if (!this.isShow({x: i, y: j})) continue;

        let td = this.getTdByPoint({x: i, y: j});
        let span = this.getTdSpan(td);

        let maxx = i + span.rowspan - 1;
        let maxy = j + span.colspan - 1;

        if (maxx >= point.x && maxy >= point.y) {
          return {
            x: i,
            y: j
          };
        }
      }
    }
  }

  initSubTableContents() {
    let trs = $(`#${this.rootElementId}`).find('tr');
    let header = trs[0], footer = trs[trs.length - 1];
    let td = $(header).find('td');
    if (td && td.length >= 1) td[0].innerHTML = '表头';
    td = $(footer).find('td');
    if (td && td.length >= 1) td[0].innerHTML = '表尾';

    for (let i = 1; i < trs.length - 1; i++) {
      let tr = trs[i];
      $(tr).attr('data-role', 'subtable-detail');
      td = $(tr).find('td');
      if (td && td.length >= 1) td[0].innerHTML = '明细区';
    }
  }

  SubTablefieldComponents(subFormDefinition) {
    let trs = $(`#${this.rootElementId}`).find('tr');
    let body = trs[1];
    let tds = $(body).find('td');
    let fields = subFormDefinition.fields;
    for (let i = 0; i < fields.length; i++) {
      let td = tds[i + 1];
      let field = fields[i];
      let component = DesignComponents.getComponent(field.component);
      $(td).html(component.getBody(field.id));
    }
  }

  expanByCell(point) {
    let minx = point.x;
    let miny = point.y;

    let td = this.getTdByPoint(point);
    if (!td) return;
    let span = this.getTdSpan(td);

    let maxx = point.x + span.rowspan;
    let maxy = point.y + span.colspan;

    for (let i = minx; i < maxx; i++) {
      for (let j = miny; j < maxy; j++) {
        this.addSelectPoint({x: i, y: j});
      }
    }
  }

  baseExpanSelection() {
    let minx = Math.min(...this.selection.selecting.map(m => m.x));
    let miny = Math.min(...this.selection.selecting.map(m => m.y));

    let maxx = Math.max(...this.selection.selecting.map(m => m.x));
    let maxy = Math.max(...this.selection.selecting.map(m => m.y));
    for (let i = minx; i <= maxx; i++) {
      for (let j = miny; j <= maxy; j++) {
        this.addSelectPoint({x: i, y: j});
      }
    }
  }

  rowBarDragstart(e) {
    if (!this.isLeftMouseButton(e)) return;

    let index = parseInt(e.target.getAttribute('data-index'));
    let start = e.clientY;
    this.rowBarFollowerPointer(e);

    let that = this;

    function fun(e) {
      that.prevent(e);
      $(`#${that.rootElementId} *`).css('cursor', '');
      let temp = e.clientY - start;
      temp = that.resolveHeightDelta(temp, index);
      that.configs.rowHeight[index] += temp;
      that.heightChange();

      $(document).unbind('mouseup', fun);
      that.clearBarFollowerPointer();
      that.resized();
    }

    $(document).on('mouseup', fun);

    this.prevent(e);
  }

  rowBarFollowerPointer(e) {
    this.rowBarPlaceholder = $(`<div class="row-bar-placeholder" style="top: ${e.clientY}px"></div>`)[0];
    document.body.append(this.rowBarPlaceholder);

    let that = this;
    this.rowBarFollowerFunction = function (e) {
      $(that.rowBarPlaceholder).css('top', `${e.clientY}px`);
    };

    $(document).on('mousemove', this.rowBarFollowerFunction);
  }

  colBarDragstart(e) {
    if (!this.isLeftMouseButton(e)) return;

    let index = parseInt(e.target.getAttribute('data-index'));
    let start = e.clientX;

    this.colBarFollowerPointer(e);

    $(`#${this.rootElementId} *`).css('cursor', 'col-resize');

    let that = this;

    function fun(e) {
      that.prevent(e);
      $(`#${that.rootElementId} *`).css('cursor', '');

      let temp = e.clientX - start;
      temp = that.resolveWidthDelta(temp, index);
      that.configs.colWidth[index] += temp;
      that.configs.colWidth[index + 1] -= temp;
      that.widthChange();

      $(document).unbind('mouseup', fun);
      that.clearBarFollowerPointer();
      that.resized();
    }

    $(document).on('mouseup', fun);
    this.prevent(e);
  }

  colBarFollowerPointer(e) {
    this.colBarPlaceholder = $(`<div class="col-bar-placeholder" style="left: ${e.clientX}px"></div>`)[0];
    document.body.append(this.colBarPlaceholder);

    let that = this;
    this.colBarFollowerFunction = function (e) {
      $(that.colBarPlaceholder).css('left', `${e.clientX}px`);
    };

    $(document).off('mousemove').on('mousemove', this.colBarFollowerFunction);
  }

  clearBarFollowerPointer() {
    $(this.colBarPlaceholder).remove();
    this.colBarPlaceholder = null;

    $(this.rowBarPlaceholder).remove();
    this.rowBarPlaceholder = null;

    $(document).unbind('mousemove', this.rowBarFollowerFunction);
    $(document).unbind('mousemove', this.colBarFollowerFunction);
  }

  prevent(e) {
    e.preventDefault();
    e.stopPropagation();
  }

  heightChange() {
    let trs = $(`#${this.rootElementId} tr`);
    for (let i = 0; i < trs.length; i++) {
      $(trs[i]).height(this.configs.rowHeight[i]);
    }

    this.reComputeHeights();
    this.refreshBarContainer();
  }

  widthChange() {
    let cols = $(`#${this.rootElementId} col`);
    for (let i = 0; i < cols.length; i++) {
      $(cols[i]).width(this.configs.colWidth[i]);
    }
    this.resolveBlankWidth();
    this.reComputeHeights();
    this.resolveConfigFromElements();
    this.refreshBarContainer();
  }

  refreshBarContainer() {
    let barContainer = this.createBarsContainer();
    if ($(`#${this.rootElementId} .bar-container`)[0]) {
      $(`#${this.rootElementId} .bar-container`)[0].replaceWith(barContainer);
    } else {
      $(`#${this.rootElementId} table`).before(barContainer);
    }
    this.bindResize();
  }

  resolveWidthDelta(delta, index) {
    let a = this.configs.colWidth[index], b = this.configs.colWidth[index + 1];
    let sum = a + b;
    let [c, d] = [a, b];
    a += delta;
    b -= delta;
    if (a < 5) {
      a = 5;
      b = sum - a;
    }
    if (b < 5) {
      b = 5;
      a = sum - b;
    }
    return a - c;
  }

  resolveHeightDelta(delta, index) {
    let a = this.configs.rowHeight[index];
    a += delta;
    if (a < 5) {
      a = 5;
    }
    return a - this.configs.rowHeight[index];
  }

  getDefaultValues() {
    return {
      col: 6,
      row: rows,
      colWidth: [],
      rowHeight: []
    };
  }

  hiddenBars(barContainer) {
    if (!$(`#${this.rootElementId} tr`).length) {
      return;
    }

    this.forEachTds((row, col, td) => {
      if (!this.isTdShow(td)) return;
      let span = this.getTdSpan(td);
      if (span.rowspan === 1 && span.colspan === 1) return;
      let minx = row;
      let miny = col;
      let maxx = row + span.rowspan;
      let maxy = col + span.colspan;

      for (let i = minx; i < maxx; i++) {
        for (let j = miny; j < maxy; j++) {
          let bar = this.getColBarByPoint({x: i, y: j}, barContainer);
          if (j < maxy - 1) {
            this.hideBar(bar);
          }

          bar = this.getRowBarByPoint({x: i, y: j}, barContainer);
          if (i < maxx - 1) {
            this.hideBar(bar);
          }
        }
      }
    });
  }

  renderTable() {
    $(`#${this.rootElementId}`).addClass('david-table');
    $(`#${this.rootElementId}`).append(this.createBarsContainer());
    $(`#${this.rootElementId}`).append(this.createTable());
  }

  createBarsContainer() {
    let barContainer = $("<div class='bar-container'></div>")[0];

    let colBarContainer = $("<div class='col-bar-container'></div>")[0];
    $(colBarContainer).append(this.createColBars());

    let rowBarContainer = $("<div class='row-bar-container'></div>")[0];
    $(rowBarContainer).append(this.createRowBars());

    $(barContainer).append(rowBarContainer);
    $(barContainer).append(colBarContainer);

    this.hiddenBars(barContainer);
    return barContainer;
  }

  createColBars() {
    let res = [], left = this.configs.colWidth[0] - 2, top = 0;
    for (let i = 0; i < this.configs.row; i++) {
      left = this.configs.colWidth[0] - 2;
      for (let j = 1; j < this.configs.col; j++) {
        let bar = $(`<div class='bar col-bar ignore-drag' data-position="${i}-${j - 1}" data-index="${j - 1}"
                    style="left: ${left}px; top: ${top}px;
                    height: ${this.configs.rowHeight[i]}px"></div>`)[0];
        left += this.configs.colWidth[j];
        res.push(bar);
      }
      top += this.configs.rowHeight[i];
    }

    return res;
  }

  createRowBars() {
    let res = [], top = -2, left = 0;

    for (let i = 0; i < this.configs.row; i++) {
      left = 0;
      top += this.configs.rowHeight[i];
      for (let j = 0; j < this.configs.col; j++) {
        let bar = $(`<div class='bar row-bar ignore-drag' data-position="${i}-${j}" data-index="${i}"
                    style="left: ${left}px; top: ${top}px; 
                    width: ${this.configs.colWidth[j]}px"></div>`)[0];

        left += this.configs.colWidth[j];
        res.push(bar);
      }
    }

    return res;
  }

  createTable() {
    let table = $(`<table class="ignore-drag"></table>`)[0];
    table.append(this.createColgroup());

    for (let i = 0; i < this.configs.row; i++) {
      let tr = this.createTr(i);
      table.append(tr);
    }

    return table;
  }

  createColgroup() {
    let colGroup = $(`<colgroup></colgroup>`)[0];

    for (let i = 0; i < this.configs.colWidth.length; i++) {
      let col = $(`<col style="width: ${this.configs.colWidth[i]}px"></col>`)[0];
      colGroup.append(col);
    }

    return colGroup;
  }

  resolveDefaultConfigValue() {
    let defaultValues = this.getDefaultValues();
    if (!this.configs) this.configs = defaultValues;
    else {
      this.mergeValues(defaultValues, this.configs);
    }

    this.configs.colWidth = this.getColWidth();
    this.configs.rowHeight = this.getRowHeight();
  }

  mergeValues(src, dist) {
    for (let key in src) {
      if (dist[key] === undefined) {
        dist[key] = src[key];
      }
    }
  }

  getColWidth() {
    let containerWidth = $(`#${this.rootElementId}`).width();
    let res = [];
    let wid = parseInt(containerWidth / this.configs.col), sum = 0;
    for (let i = 0; i < this.configs.col - 1; i++) {
      sum += wid;
      res.push(wid);
    }

    res.push(containerWidth - sum);
    return res;
  }

  createTr(row) {
    let tr = $(`<tr style="height: ${this.configs.rowHeight[row]}px"></tr>`)[0];
    for (let i = 0; i < this.configs.col; i++) {
      let td = this.createTd(row, i);
      tr.append(td);
    }
    return tr;
  }

  createTd(row, col) {
    let td = $(`<td data-role="block-container"
                data-single-child="true" style="font-size: 10px"
                 data-pointion="${row}-${col}"></td>`)[0];
    return td;
  }

  getRowHeight() {
    let res = [];
    for (let i = 0; i < this.configs.row; i++) {
      res.push(defaultHeight);
    }
    return res;
  }

  getPosition(td) {
    let p = td.getAttribute('data-pointion');
    p = p.split('-');
    return {
      x: parseInt(p[0]),
      y: parseInt(p[1])
    };
  }

  getTdByPoint({x, y}) {
    let selectStr = `#${this.rootElementId} [data-pointion='${x}-${y}']`;
    let td = $(selectStr);
    if (td && td.length > 0) return td[0];
    return null;
  }

  isShow(point) {
    let td = this.getTdByPoint(point);
    return $(td).css('display') !== 'none';
  }

  isTdShow(td) {
    return $(td).css('display') !== 'none';
  }

  getTdSpan(td) {
    if (!td) {
      console.log(td);
    }
    let colspan = td.getAttribute('colspan');
    if (!colspan) colspan = 1;
    let rowspan = td.getAttribute('rowspan');
    if (!rowspan) rowspan = 1;

    colspan = parseInt(colspan + '');
    rowspan = parseInt(rowspan + '');
    return {colspan, rowspan};
  }

  isSelecting(point) {
    let index = this.selection.selecting
    .findIndex(p => p.x === point.x && p.y === point.y);
    return index !== -1;
  }

  isLeftMouseButton(e) {
    return e.buttons === 1;
  }

  isRightMouseButton(e) {
    return e.buttons === 1;
  }

  colSpanAddOne(td) {
    let span = this.getTdSpan(td);
    span.colspan++;
    this.setTdSpan({td, span});
  }

  setTdSpan({td, span}) {
    if (this.isTdShow(td)) {
      $(td).attr('colspan', span.colspan);
      $(td).attr('rowspan', span.rowspan);
    } else {
      $(td).attr('colspan', '');
      $(td).attr('rowspan', '');
    }
  }

  forEachTds(callback) {
    let trs = $(`#${this.rootElementId} tr`);
    for (let i = 0; i < trs.length; i++) {
      let tds = $(trs[i]).find('td');
      for (let j = 0; j < tds.length; j++) {
        callback(i, j, tds[j]);
      }
    }
  }

  hideTd(td) {
    $(td).html('');
    $(td).css('display', 'none');
  }

  showTd(td) {
    $(td).css('display', '');
  }

  isEditable(td) {
    return $(td).attr('contentEditable') === 'true';
  }

  editableTd(td) {
    $(td).attr('contentEditable', true);
  }

  removeEditTd(td) {
    $(td).removeAttr('contentEditable');
  }

  getColBarByPoint({x, y}, barContainer) {
    let bar = $(barContainer).find(`.col-bar-container [data-position="${x}-${y}"]`);
    return bar[0];
  }

  getRowBarByPoint({x, y}, barContainer) {
    let bar = $(barContainer).find(`.row-bar-container [data-position="${x}-${y}"]`);
    return bar[0];
  }

  hideBar(bar) {
    if (bar)
      $(bar).css('display', 'none');
  }

  showBar(bar) {
    if (bar)
      $(bar).css('display', '');
  }

  isMultiplyColSpan(td) {
    return this.getTdSpan(td).colspan > 1;
  }

  isMultiplyRowSpan(td) {
    return this.getTdSpan(td).rowspan > 1;
  }

  clearContainer() {
    $(`#${this.rootElementId}`).html('');
  }

  resolveConfigFromElements() {
    let trs = $(`#${this.rootElementId} tr`);
    let cols = $(`#${this.rootElementId} colgroup col`);
    this.configs.row = trs.length;
    this.configs.col = cols.length;
    this.configs.rowHeight = [];
    this.configs.colWidth = [];

    for (let i = 0; i < trs.length; i++) {
      this.configs.rowHeight.push($(trs[i]).height());
    }

    for (let i = 0; i < cols.length; i++) {
      this.configs.colWidth.push($(cols[i]).width());
    }
  }

  refreshWidthKeepPercent() {
    let colWidth = this.configs.colWidth;
    let containerWidth = $(`#${this.rootElementId}`).width();
    let sum = 0;
    for (let i = 0; i < colWidth.length; i++) sum += colWidth[i];
    let per = containerWidth * 1.0 / sum;

    let s = 0;
    for (let i = 0; i < colWidth.length; i++) {
      colWidth[i] = parseInt(colWidth[i] * per);
      s += colWidth[i];
    }
    s -= colWidth[colWidth.length - 1];
    colWidth[colWidth.length - 1] = containerWidth - s;

    this.resolveTdPosition();
    this.resolveRowCount();
    this.widthChange();
    this.resolveConfigFromElements();
    this.widthChange();
    this.reComputeHeights();
    this.resolveBlankWidth();
    this.refreshBarContainer();
    this.heightChange();

    this.resolveConfigFromElements();
    this.refreshBarContainer();
  }

  resolveRowCount() {
    let count = $(`#${this.rootElementId} tr`).length;
    while (this.configs.rowHeight.length < count) {
      this.configs.rowHeight.push(defaultHeight);
    }
    this.configs.row = count;
  }

  resolveBlankWidth() {
    let sum = 0, list = this.configs.colWidth;
    let width = $(`#${this.rootElementId}`).width();
    for (let i = 0; i < list.length; i++) {
      sum += list[i];
    }
    sum -= list[list.length - 1];
    list[list.length - 1] = width - sum;
  }

  hasPosition(target) {
    return !!target.getAttribute('data-pointion');
  }

  tryGetPositionTd(target) {
    while (target && !this.hasPosition(target)) {
      target = target.parentNode;
    }
    return target;
  }

  clearStyle() {
    let selecting = this.selection.selecting;
    let trs = $(`#${this.rootElementId} tr`);

    for (let i = 0; i < selecting.length; i++) {
      let td = this.getTdByPoint(selecting[i]);
      this.clearTdStyle(td, trs);
    }
    this.clearSelection();
    this.clearStyled();
  }

  clearTdStyle(td, trs) {
    $(td).attr('style', '');
    $(td).attr('colspan', '');
    $(td).attr('rowspan', '');

    let {x, y} = this.getPosition(td);
    this.configs.rowHeight[x] = defaultHeight;
    this.heightChange();
    this.reComputeHeights();
  }

  resized() {
    if (this.configs.onViewChange) {
      this.configs.onViewChange();
    }
  }

  edited() {
    if (this.configs.onViewChange) {
      this.configs.onViewChange();
    }
  }

  merged() {
    if (this.configs.onViewChange) {
      this.configs.onViewChange();
    }
  }

  disassembled() {
    if (this.configs.onViewChange) {
      this.configs.onViewChange();
    }
  }

  inserted() {
    if (this.configs.onViewChange) {
      this.configs.onViewChange();
    }
  }

  removed() {
    if (this.configs.onViewChange) {
      this.configs.onViewChange();
    }
  }

  clearStyled() {
    if (this.configs.onViewChange) {
      this.configs.onViewChange();
    }
  }
}

$.contextMenu = function (options) {
  class ContextMenu {
    constructor(setting) {
      this.init(setting);
    }

    init(setting) {
      let defaults = {
        menu: [],
        left: 0,
        top: 0,
        withLeftBind: false
      };
      let settings = $.extend({}, defaults, options);
      let menuDiv = this.menuDiv = $('<div></div>');
      let shadeDiv = this.shadeDiv = $('<div class="ms-right-shade"></div>');
      menuDiv.attr('class', 'ms-right-wrap');
      for (let i = 0; i < settings.menu.length; i++) {
        if (typeof settings.menu[i].type == "undefined") {
          settings.menu[i].type = 'menu';
        }

        if (settings.menu[i].type === 'menu') {
          let menuItem = $('<li></li>');
          menuItem.addClass('ms-item');
          menuItem.attr('data-item', i);
          menuItem.appendTo(menuDiv);
          menuItem.html(settings.menu[i].itemName);
          menuItem.mousedown(e => {
            e.stopPropagation();
            e.preventDefault();
          });
          menuItem.click(function (e) {
            if (!settings.menu[i].click) {
              shadeDiv.remove();
              menuDiv.remove();
            } else if (settings.menu[i].click(e)) {
              shadeDiv.remove();
              menuDiv.remove();
            }
          });
        } else if (settings.menu[i].type === 'line') {
          let menuItem = $('<li></li>');
          menuItem.addClass('ms-line');
          menuItem.appendTo(menuDiv);
        }
        menuDiv.prependTo('body');
        menuDiv.css({
          'display': 'block',
          'top': settings.top + 'px',
          'left': settings.left + 'px'
        });
      }
    }

    close() {
      this.shadeDiv.remove();
      this.menuDiv.remove();
    }
  }

  return new ContextMenu(options);
};

export default DavidTable;
